package com.wy2cloud.jjwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;


import com.wy2cloud.core.tools.StringTools;
import com.wy2cloud.core.web.CookieTools;

/**
 * JsonWebToken 工具集
 * @author wuyy
 *@version 2017-9-22
 */
public class JsonWebTokenUtil implements Serializable {

    private static final long serialVersionUID = -3301605591108950415L;

    public static final String CLAIM_KEY_USERNAME = "sub";
    public static final String CLAIM_KEY_CREATED = "created";

    private final String secret;
    private final Long expiration;
    private final String requestParam;
    private final String cookieName;
    private final String tokenHeader;
    private final String tokenHead;
    private final String[] tokenStores; //header,cookie,url
    
    

    public JsonWebTokenUtil(String secret, Long expiration,String requestParam,String cookieName,String tokenHeader,String tokenHead,String[] tokenStores) {
		super();
		this.secret = secret;
		this.expiration = expiration;
		this.requestParam=requestParam;
		this.cookieName=cookieName;
		this.tokenHeader=tokenHeader;
		this.tokenHead=tokenHead;
		this.tokenStores=tokenStores;
	}
    
    public String getSecret() {
		return secret;
	}

	public Long getExpiration() {
		return expiration;
	}

	public String getRequestParam() {
		return requestParam;
	}

	public String getCookieName() {
		return cookieName;
	}

	public String getTokenHeader() {
		return tokenHeader;
	}
	public String getTokenHead() {
		return tokenHead;
	}
	public String[] getTokenStores() {
		return tokenStores;
	}

	/**
	 * 根据载体内容，获得token
	 * @param payload
	 * @return
	 */
	public String getTokenFromPayload(String payload) {
		if(StringTools.isEmpty(payload)) return StringTools.Empty;
		if(payload.startsWith(tokenHead)) {
			return payload.substring(tokenHead.length());
		}
		return StringTools.Empty;
	}

	public String getUsernameFromToken(String token) {
        String username;
        try {
            final Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    public Date getCreatedDateFromToken(String token) {
        Date created;
        try {
            final Claims claims = getClaimsFromToken(token);
            created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
        } catch (Exception e) {
            created = null;
        }
        return created;
    }

    public Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            final Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }

    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }
    
    public Object getValueFromToken(String token,String key) {
    	Object val;
        try {
            final Claims claims = getClaimsFromToken(token);
            val = claims.get(key);
        } catch (Exception e) {
        	val = null;
        }
        return val;
    }
    public Map<String,Object> getMapFromToken(String token) {
    	Map<String,Object> val;
        try {
            final Claims claims = getClaimsFromToken(token);
            val = claims;
        } catch (Exception e) {
        	val = null;
        }
        return val;
    }

    private Date generateExpirationDate(Long tokenExpiration) {
    	if(tokenExpiration<=0) tokenExpiration=expiration;
        return new Date(System.currentTimeMillis() + tokenExpiration * 1000);
    }

    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
        //return (lastPasswordReset != null && created.before(lastPasswordReset));
    	return false;
    }

    public String generateToken(String userName) {
    	return generateToken(userName,0L,null);
    }
    public String generateToken(String userName,Long tokenExpiration,Map<String,Object> moreInfo) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, userName);
        claims.put(CLAIM_KEY_CREATED, new Date());
        if(moreInfo!=null) {
        	claims.putAll(moreInfo);
        }
        return generateToken(claims,tokenExpiration);
    }

    private String generateToken(Map<String, Object> claims,Long tokenExpiration) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate(tokenExpiration))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
        final Date created = getCreatedDateFromToken(token);
        return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
                && !isTokenExpired(token);
    }

    public String refreshToken(String token,Long tokenExpiration) {
        String refreshedToken;
        try {
            final Claims claims = getClaimsFromToken(token);
            claims.put(CLAIM_KEY_CREATED, new Date());
            refreshedToken = generateToken(claims,tokenExpiration);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }

    public Boolean validateToken(String token, String userName,Date lastPasswordResetDate) {
        final String username = getUsernameFromToken(token);
        final Date created = getCreatedDateFromToken(token);
        //final Date expiration = getExpirationDateFromToken(token);
        return (
                username.equals(userName)
                        && !isTokenExpired(token)
                        && !isCreatedBeforeLastPasswordReset(created, lastPasswordResetDate));
    }
    public Boolean validateToken(String token) {
        return !isTokenExpired(token);
    }
    
    public String getAuthToken(HttpServletRequest request) {
    	String authHeader = request.getHeader(getTokenHeader());
    	
    	if(StringTools.isEmpty(authHeader)) {
    		authHeader=CookieTools.getCookieValue(request, this.getCookieName());
    	}
    	
    	if(StringTools.isEmpty(authHeader)) {
    		authHeader=request.getParameter(this.getRequestParam());
    	}
    	
        
    	if(StringTools.isEmpty(authHeader)) {
    		return StringTools.Empty;
    	}
    	 String authToken=this.getTokenFromPayload(authHeader);
    	 return authToken;
    }
}